function handleSuccessfullTreeRetrieval( tree ) {

    var localGlobal = globals;
    
    localGlobal.tree = eval( "(" + tree + ")" );

    localGlobal.tree.isRoot = true;

    localGlobal.scrollBarWidth = getScrollBarWidth();

    grafterInitialize();
    
    grafterWindowOnLoad();
}

function setSkeletonCanvasSize( p ) {

    var treeInfo = p.global.treeInfo;
   
    var longestLabel = getWordLength( { word: treeInfo.longestChildName, size: p.config.fontSize.value * p.scaleValue.y, canvas: p.canvas } );

    treeInfo.longestChildLabel = longestLabel.width;
    treeInfo.exampleLabelHeight = longestLabel.height;

    var width = p.treeWidth + 
                ( p.config.canvasBuffer.value * 2 ) +
                ( p.config.labelBuffer.value ) +
                ( p.global.scrollBarWidth ) +
                ( treeInfo.longestChildLabel ) +
                ( treeInfo.rootLabelLength ) + 20; //add the 20 for the root

    var height = ( ( treeInfo.tips - 1 ) * p.config.nodeSeparation.value ) + ( longestLabel.height * 2 ) +
                 ( p.config.canvasBuffer.value * 2 );

    // 100% size
    p.global.canvas = { width: width, height: height };

    var mainPanelWidth = $('#mainPanel').width();
    if( width < mainPanelWidth ) { width = mainPanelWidth; }

    $('#' + p.container).width( width ).height( height );

    p.canvas.setSize( width, height );
}

function grafterInitialize() {

    var localGlobal = globals;
    var localConfig = config;

    //if canvas does not exist yet - create it
    if( !localGlobal.mainPanelCanvas ) { localGlobal.mainPanelCanvas = createSkeletonCanvas( { container: 'skeletonContainer', id: 'skeletonCanvas' } ); }
    
    //traverse the tree for information needed before rendering 
    getTreeInfo( { tree: localGlobal.tree } );
    
    //width of tree only 
    var treeWidth = ( localConfig.generationSeparation.value * ( localGlobal.treeInfo.generations - 1 ) );

    setSkeletonCanvasSize( { canvas: localGlobal.skeletonCanvas,
                             treeWidth: treeWidth,
                             global: localGlobal,
                             config: localConfig,
                             container: 'skeletonContainer',
                             scaleValue: { x: 1, y: 1 } } );

    if( localGlobal.scaling == 'scaled' ) { localGlobal.lengthMultiplier = treeWidth / localGlobal.treeInfo.longestTraversal; }

    removeExpandableIcons( { main: true, overview: false } );
}

function createSkeletonCanvas( p ) {

    //create canvas inside raphaelContainer div
    var canvasObj = Raphael( p.container, 300, 100 );
       
    //assign id to canvas DOM  object
    $('#' + p.container + 'svg').attr( { id: p.id } );

    return canvasObj;
}

function grafterWindowOnLoad() {

    var localGlobal = globals;
    var localConfig = config;

    addShadowboxKeyPressHandlers();

    drawSkeletonTree( { global: localGlobal, config: localConfig } );

    $(document).ready( onGrafterDocumentReady );

    $('#mainPanel').mouseup( handleMainPanelMouseUp );
}

function handleMainPanelMouseUp( p ) {
        
    var contextMenu = isContextMenu();

    if( p.which == 3 ) {
    
        if( contextMenu ) { contextMenu.empty().remove(); return; }

        var localGlobal = globals;

        if( localGlobal.pathToPiece.isVisible ) {

            var pieceId;

            var containers = $('#pieceTreeContainer .visualHolder');

            if( containers.length == 1 ) {

                pieceId = $(containers[0]).parent().attr('id');

            } else { 

                for( var container in $('#pieceTreeContainer .visualHolder') ) {
                    container = $(container);
                    if( container.is(':visible') ) {
                        pieceId = container.parent().attr('id');
                        break;
                    }
                }
            }

            makeContextMenu( { container: $('#mainPanel'), 
                               menuItems: [ { text: 'Graft Clade',
                                              func: graftClade,
                                              params: { skeletonNodeId: localGlobal.pathToPiece.closestCladeId,
                                                        pieceNodeId: pieceId } } ],
                               x: p.pageX, y: p.pageY } );
        }
    } else {
        
        if( contextMenu && mouseOnElement( { x: p.pageX, y: p.pageY, el: contextMenu } ) ) { return true; }

    }

}

function graftClade( p ) {

    $.ajax( { url: makeAjaxURL( { name: 'graftClade' } ),
              type: "POST",
              data: p.data,
              success: handleSuccessfulGraft } );
}

function handleSuccessfulGraft( p ) {

}

function onGrafterDocumentReady() {

    var mainPanel = $('#mainPanel');

    createGrafterHomeLink( { mainPanel: mainPanel } );

    initializeGrafterScroll( { mainPanel: mainPanel } );

    $(window).resize( handleGrafterWindowResize );

    //retrieveSavedClades();
}

function retrieveSavedClades() {

    $.ajax( { url: '/' + globals.applicationName + '/default/retrieveSavedCladesInfo',
              type: "POST",
              data: { },
              success: handleReceivedPieceTreeInfo } );
}

function createTreeInfoHeader( p ) {

    return makeEl('div').attr( { 'id': p.id, 'class': 'bottomBorder' } ).append(
               makeEl('div').attr( { 'class': 'padding10p' } )
                            .bind( 'click', { id: p.id }, handleTreeHeaderClick )
                            .mouseover( addContextHighlight )
                            .mouseout( removeContextHighlight ).append(
                    makeEl('div').attr( { 'class': 'fLeft' } ).text( p.name ),
                    makeEl('div').attr( { 'class': 'fRight' } ).text( p.creationDate ),
                    makeEl('div').attr( { 'class': 'clear' } ) ) );
}



function handleReceivedPieceTreeInfo( response ) {

    var pieceTreeInfo = eval( "(" + response + ")" );

    delete response;

    var pieceTreeInfoContainer = makeEl('div').attr( { 'id': 'pieceTreeContainer', 'class': 'scrollable height90' } );

    for( var i = 0, ii = pieceTreeInfo.length; i < ii; i++ ) {

        pieceTreeInfoContainer.append(
            makeEl('div').attr( { 'id': pieceTreeInfo[i].id, 'class': 'bottomBorder' } ).append(
                makeEl('div').attr( { 'class': 'padding10p' } )
                             .bind( 'click', { id: pieceTreeInfo[i].id }, handlePieceHeaderClick )
                             .mouseover( addContextHighlight )
                             .mouseout( removeContextHighlight ).append(
                    makeEl('div').attr( { 'class': 'fLeft' } ).text( pieceTreeInfo[i].name ),
                    makeEl('div').attr( { 'class': 'fRight' } ).text( pieceTreeInfo[i].creationDate ),
                    makeEl('div').attr( { 'class': 'clear' } ) ) ) );
    }

    $('#rightPanel').append( pieceTreeInfoContainer );
}

function addCursor() { $(this).addClass('pointer'); }
function removeCursor() { $(this).removeClass('pointer'); }

function handlePieceHeaderClick( p ) {

   hideVisualGraftingAnnotations();

   var id = p.data.id;
       
   hidePieceTreeContainers();

   if( $('#' + id).children('div').length == 1 ) {

        $.ajax( { url: '/' + globals.applicationName + '/default/retrieveSavedClade',
                  type: "POST",
                  data: { nodeId: id },
                  success: handleReceivedPieceTree } );

   } else if( $('#visualHolder' + id).is(':hidden') ) {

       $('#visualHolder' + id).show('slow');

   } else {
       
       $('#visualHolder' + id).hide('slow');
   }
}

function hidePieceTreeContainers() {

    var pieceTreeContainers = $('.visualHolder');

    for( var i = 0, ii = pieceTreeContainers.length; i < ii; i++ ) {

        var container = $(pieceTreeContainers[i]);

        if( ! container.is(':hidden') ) {
        
            $(container.children()[0]).removeAttr('style');

            container.hide('slow');
        }
    }
}


function handleReceivedPieceTree( response ) {

    var localGlobal = globals;

    var responseData = eval( "(" + response + ")" );

    responseData.tree.isRoot = true;

    var containerId = 'pieceTreeVisual' + responseData.id;

    var visualContainer = makeEl('div').attr( { 'id': containerId, 'class': 'absoluteFront' } );

    responseData.tree.meta = getPieceTreeRenderInfo( { 'clade': responseData.tree } );

    var canvasHeight = $('#pieceTreeContainer').height() / 2;
    var canvasWidth = $('#pieceTreeContainer').width() * .95;
    
    $('#' + responseData.id).append(
        makeEl('div').attr( { 'id': 'visualHolder' + responseData.id, 'class': 'visualHolder' } )
                     .height( canvasHeight )
                     .width( canvasWidth )
                     .css( { 'display': 'none' } )
                     .append ( visualContainer ) );

    localGlobal.pieceTreeInfo[ responseData.id ] = { 'canvas': Raphael( containerId, canvasWidth, canvasHeight ) };

    var generationSeparation = ( ( canvasWidth - 20 ) / ( responseData.tree.meta.generations ) );
    var nodeSeparation = ( canvasHeight / ( responseData.tree.meta.tips + 1 ) );

    var firstTip = { x: ( generationSeparation * ( responseData.tree.meta.generations - 1 ) + 20 ), //add 20 for root
                     y: nodeSeparation };


    var pathString = new StringBuffer();
    
    var scaledBranches = ( responseData.scaling == 'scaled' ) ? true : false;

    preRender( { clade: responseData.tree,
                 scaledBranches: scaledBranches,
                 currentTip: firstTip,
                 scaleValue: { x: 1, y: 1 },
                 generationSeparation: generationSeparation,
                 nodeSeparation: nodeSeparation,
                 lengthMultiplier: responseData.tree.meta.lengthMultiplier,
                 scaling: responseData.scaling,
                 pathWidth: 2,
                 fontSize: 10,
                 buttonRadius: 0,
                 labelBuffer: 0,
                 pathStrings: pathString,
                 renderInfoFun: pieceGatherCladeRenderInfo,
                 canvas: localGlobal.pieceTreeInfo[ responseData.id ].canvas,
                 generation: responseData.tree.meta.generations } );


    if( responseData.scaling == 'smooth') {

        findSmoothCoords( { clade: responseData.tree,
                            scaling: responseData.scaling,
                            fontSize: 10,
                            buttonRadius: 0,
                            labelBuffer: 0,
                            pathWidth: 2,
                            renderInfoFun: pieceGatherCladeRenderInfo,
                            pathStrings: pathString,
                            canvas: localGlobal.pieceTreeInfo[ responseData.id ].canvas,
                            leftmostStart: canvasWidth } );
    }

    createTreePath( { string: pathString.toString(),
                      canvas: localGlobal.pieceTreeInfo[ responseData.id ].canvas,
                      attr: { stroke: 'black',
                              "stroke-width": 2 } } );

    visualContainer.attr( { 'rootY': responseData.tree.coords.y,
                            'rootX': responseData.tree.coords[responseData.scaling].x,
                            'scaling': responseData.scaling } );


    visualContainer.draggable( { containment: 'document', 
                                 drag: handlePieceDrag,
                                 start: setPieceDragging,
                                 stop: unsetPieceDragging } );

    $('#visualHolder' + responseData.id).show('slow');
}


function handlePieceDrag( p ) {

    var localGlobal = globals;
    var localConfig = config;

    var movingBox = $(p.target);
    var boxOffset = movingBox.offset();

    var mainPanel = $('#mainPanel');
    var mainPanelOffset = mainPanel.offset();

    var skeletonTreeContainer = $('#skeletonContainer');

    if( skeletonTreeContainer.height() >  mainPanel.height() ) {

        if( ( mainPanelOffset.top + mainPanel.height() ) - ( boxOffset.top + movingBox.height() ) < 2 ) {

            if( ! localGlobal.scrollEvents.down ) {
                localGlobal.scrollEvents.down = true;

                setTimeout( verticalScroll, 0, { mainPanel: mainPanel, scroll: 30 } );
            }
            
        } else {

            localGlobal.scrollEvents.down = false;
        }


        if( ( boxOffset.top - mainPanelOffset.top ) < 2 ) {

            if( !localGlobal.scrollEvents.up ) {

                localGlobal.scrollEvents.up = true;

                setTimeout( verticalScroll, 0, { mainPanel: mainPanel, scroll: -30 } );
            }

        } else {
            
            localGlobal.scrollEvents.up = false;
        }
    }

    var closestCladeInfo = 
        findClosestClade(
            { x: boxOffset.left, y: parseInt(boxOffset.top) + parseInt(movingBox.attr('rootY')),
              closestClade: undefined, currentDistance: 1000000,
              scaleValue: { x: 1, y: 1 }, scaling: localGlobal.scaling, clade: localGlobal.tree,
              offset: { y: mainPanelOffset.top - mainPanel.scrollTop(),
                        x: mainPanelOffset.left - mainPanel.scrollLeft() } } );

    var recentLabelId = localGlobal.labelInfo.recentlyMovedId;

    if( closestCladeInfo.distance < 100 ) {

        var closestClade = closestCladeInfo.clade;

        if( recentLabelId && ( closestClade.id != recentLabelId ) ) {

            var labelToMove = localGlobal.labelInfo.labels[recentLabelId];

            translateLabel( { clade: findClade( { clade: localGlobal.tree, id: recentLabelId } ),
                              label: labelToMove,
                              fontSize: localConfig.fontSize.value,
                              buttonRadius: 0,
                              labelBuffer: 0,
                              scaling: localGlobal.scaling,
                              scaleValue: { x:1, y:1 } } );

            recentLabelId = undefined;
        }
        
        if( closestClade.children.length == 0 ) {

            var pathString = new StringBuffer();
                
            grafterAddNodeToTreePath( { pathStrings: pathString,
                                        clade: closestClade,
                                        scaling: localGlobal.scaling } );

            if( localGlobal.pathToHide ) {

                localGlobal.pathToHide.attr( { path: pathString.toString() } ).show();

            } else {

                localGlobal.pathToHide = createTreePath( { string: pathString.toString(),
                                                           canvas: localGlobal.skeletonCanvas,
                                                           attr: { stroke: 'white',
                                                                  "stroke-width": localConfig.pathWidth.value } } );
            }

            pathString = new StringBuffer();

            var coords = closestClade.coords;

            pathString.append("M").append(coords[localGlobal.scaling].x + coords[localGlobal.scaling].dx).append(" ")
                      .append(coords.y).append("L").append( boxOffset.left - mainPanelOffset.left + mainPanel.scrollLeft() ).append(" ")
                      .append( boxOffset.top - mainPanelOffset.top + mainPanel.scrollTop() + parseInt(movingBox.attr('rootY')) );

            if( localGlobal.pathToPiece ) {
                
                localGlobal.pathToPiece.attr( { path: pathString.toString() } ).show();

            } else {

                localGlobal.pathToPiece = createTreePath( { string: pathString.toString(),
                                                            canvas: localGlobal.skeletonCanvas,
                                                            attr: { stroke: 'blue',
                                                                   "stroke-width": localConfig.pathWidth.value } } );
            }

            if( closestClade.name ) {
  
                var label =  localGlobal.labelInfo.labels[closestClade.id];

                label.attr( { x: boxOffset.left - mainPanelOffset.left + mainPanel.scrollLeft() + (label.getBBox().width/2) + 5,
                              y: boxOffset.top - mainPanelOffset.top + mainPanel.scrollTop() + parseInt(movingBox.attr('rootY')) } );
                
                localGlobal.labelInfo.recentlyMovedId = closestClade.id;

            }
            
        } else {
            
            if( localGlobal.pathToHide ) { localGlobal.pathToHide.hide(); }

            pathString = new StringBuffer();

            var coords = closestClade.coords;

            var x = coords[localGlobal.scaling].x + coords[localGlobal.scaling].dx;
            var boxY = boxOffset.top - mainPanelOffset.top + mainPanel.scrollTop() + parseInt(movingBox.attr('rootY'));

            pathString.append("M").append(x).append(" ").append(coords.y).append("L").append(x).append(" ").append(boxY)
                      .append("M").append(x).append(" ").append(boxY).append("L").append(boxOffset.left - mainPanelOffset.left + mainPanel.scrollLeft())
                      .append(" ").append(boxY);

            if( localGlobal.pathToPiece ) {
                
                localGlobal.pathToPiece.attr( { path: pathString.toString() } ).show();

            } else {

                localGlobal.pathToPiece = createTreePath( { string: pathString.toString(),
                                                            canvas: localGlobal.skeletonCanvas,
                                                            attr: { stroke: 'blue',
                                                                   "stroke-width": localConfig.pathWidth.value } } );
            }
        }

        localGlobal.pathToPiece.isVisible = true;
        localGlobal.pathToPiece.closestCladeId = closestClade.id;

   } else {

       hideVisualGraftingAnnotations();

   }

}

function hideVisualGraftingAnnotations() {

    var localGlobal = globals;
    var localConfig = config;

    var recentLabelId = localGlobal.labelInfo.recentlyMovedId;

    if( localGlobal.pathToHide ) { localGlobal.pathToHide.hide(); }
    if( localGlobal.pathToPiece ) { localGlobal.pathToPiece.hide(); localGlobal.pathToPiece.isVisible = false; }

    if( recentLabelId ) {
        var labelToMove = localGlobal.labelInfo.labels[recentLabelId];

        translateLabel( { clade: findClade( { clade: localGlobal.tree, id: recentLabelId } ),
                          label: labelToMove,
                          fontSize: localConfig.fontSize.value,
                          buttonRadius: 0,
                          labelBuffer: 0,
                          scaling: localGlobal.scaling,
                          scaleValue: { x:1, y:1 } } );

        recentLabelId = undefined;
    }
}
       
function verticalScroll( p ) {

    var localGlobal = globals;

    if( ( localGlobal.scrollEvents.down || localGlobal.scrollEvents.up ) && localGlobal.draggingPiece ) {

        p.mainPanel.scrollTop( p.mainPanel.scrollTop() + p.scroll );

        setTimeout( verticalScroll, 250, { mainPanel: p.mainPanel, scroll: p.scroll } );
    }
}

function setPieceDragging() { globals.draggingPiece = true; }
function unsetPieceDragging() { globals.draggingPiece = false; }

function pieceGatherCladeRenderInfo( p ) {

    var currentCoords = p.clade.coords;

    grafterAddNodeToTreePath( { pathStrings: p.pathStrings,
                                clade: p.clade,
                                scaling: p.scaling } );
   
    if( p.clade.isExpandable == 'True' ) {

        drawExpandableIcon( { canvas: p.canvas,
                              type: 'main',
                              cladeId: p.clade.id,
                              point: { x: currentCoords[ p.scaling ].x,
                                       y: currentCoords.y },
                              size: 5 } );

    }
}

function getPieceTreeRenderInfo( p ) {

    var myTraversal = p.clade.length;
    var tips = 0;
    var myGeneration = 1;

    if( p.clade.children.length ) {

        var returnedInfo = [];
        var longestTraversal = 0;
        var mostGenerations = 0;

        for( var i = 0, ii = p.clade.children.length; i < ii; i++ ) {

            returnedInfo.push( getPieceTreeRenderInfo( { 'clade': p.clade.children[i] } ) );

            if( returnedInfo[i].traversal > longestTraversal ) {

                longestTraversal = returnedInfo[i].traversal;
            }

            if( returnedInfo[i].generations > mostGenerations ) {

                mostGenerations = returnedInfo[i].generations;
            }

            tips = tips + returnedInfo[i].tips;
        }

        myTraversal = myTraversal + longestTraversal;
        myGeneration = myGeneration + mostGenerations;

    } else {

        tips = tips + 1;
    }

    return { traversal: myTraversal,
             generations: myGeneration,
             tips: tips };
}

function createGrafterHomeLink( p ) {

    var panelOffset = p.mainPanel.offset();

    $('#mainPanel').prepend(
            makeEl('a').attr( { 'class': 'smallText', href: '/' + globals.applicationName + '/default/index' } ).text('Home')
                       .css( { position: 'absolute', left: panelOffset.left + 5, top: panelOffset.top + 5 } ) );

}

function initializeGrafterScroll( p ) {

    p.mainPanel.scrollLeft( globals.canvas.width - p.mainPanel.width() + globals.scrollBarWidth );
}
    
function handleGrafterWindowResize() { }


function drawSkeletonTree( p ) {

    var firstTip = { x: ( p.config.generationSeparation.value * ( p.global.treeInfo.generations - 1 ) + 20 + p.config.canvasBuffer.value ), //add 20 for root
                     y: ( p.config.canvasBuffer.value + p.global.treeInfo.exampleLabelHeight ) };

    var scaledBranches = ( p.global.scaling == 'scaled' ) ? true : false;

    var skeletonPathString = new StringBuffer();
    
    preRender( { clade: p.global.tree,
                 scaledBranches: scaledBranches,
                 currentTip: firstTip,
                 scaleValue: { x: 1, y: 1 },
                 generationSeparation: p.config.generationSeparation.value,
                 nodeSeparation: p.config.nodeSeparation.value,
                 lengthMultiplier: p.global.lengthMultiplier,
                 scaling: 'unscaled',
                 pathWidth: p.config.pathWidth.value,
                 fontSize: p.config.fontSize.value,
                 buttonRadius: p.config.buttonRadius.value,
                 labelBuffer: p.config.labelBuffer.value,
                 pathStrings: skeletonPathString,
                 labels: p.global.labelInfo.labels,
                 labelsToAdd: p.global.labelInfo.labelsToAdd,
                 renderInfoFun: skeletonGatherCladeRenderInfo,
                 canvas: p.global.skeletonCanvas,
                 generation: p.global.treeInfo.generations } );


    var labelInfo = p.global.labelInfo;

    if( labelInfo.labelsToAdd.length ) {

        setTimeout( grafterAddLabels,
                    0,
                    { labels: labelInfo.labelsToAdd,
                              labelHolder: labelInfo.labels,
                              canvas: p.global.skeletonCanvas,
                              scaling: p.global.scaling,
                              localConfig: { labelColor: p.config.labelColor.value,
                                             fontSize: p.config.fontSize.value,
                                             fontFaily: p.config.fontFamily.value,
                                             buttonRadius: p.config.buttonRadius.value,
                                             labelBuffer: p.config.labelBuffer.value },
                                             index: 0 } );
    }


    p.global.pathObjs.main.tree =
        createTreePath( { string: skeletonPathString.toString(),
                          canvas: p.global.skeletonCanvas,
                          attr: { stroke: p.config.cladeColor.value,
                                  "stroke-width": p.config.pathWidth.value } } );
}

function skeletonGatherCladeRenderInfo( p ) {

    var currentCoords = p.clade.coords;

    grafterAddNodeToTreePath( { pathStrings: p.pathStrings,
                                clade: p.clade,
                                scaling: p.scaling } );
   
    if( p.clade.isExpandable == 'True' ) {

        drawExpandableIcon( { canvas: p.canvas,
                              type: 'main',
                              cladeId: p.clade.id,
                              point: { x: currentCoords[ p.scaling ].x,
                                       y: currentCoords.y },
                              size: 5 } );

    }

    if( p.clade.name ) {

        handleLabel( { clade: p.clade,
                       labels: p.labels,
                       labelsToAdd: p.labelsToAdd,
                       fontSize: p.fontSize,
                       scaleValue: p.scaleValue,
                       buttonRadius: p.buttonRadius,
                       supportValueInfo: p.supportValueInfo,
                       scaling: p.scaling,
                       labelBuffer: p.labelBuffer } );
    }
}

function grafterAddNodeToTreePath( p ) {

    var coords = { x: p.clade.coords[p.scaling].x,
                  dx: p.clade.coords[p.scaling].dx,
                   y: p.clade.coords.y };

    if( p.clade.isRoot ) {

        p.pathStrings.append( "M" ).append( coords.x ).append( " " ).append( coords.y ).append( "l-20 0" );

    } else {

        p.pathStrings.append( "M" ).append( coords.x ).append( " " ).append( coords.y ).append( "l" ).append( coords.dx ).append( " 0" );
    }

    if( p.clade.children.length ) {

        var firstChildY = p.clade.children[0].coords.y;
        var lastChildY = p.clade.children[p.clade.children.length-1].coords.y;

        p.pathStrings.append( "M" ).append( coords.x ).append( " " ).append( firstChildY)
                     .append( "L" ).append( coords.x ).append( " " ).append( lastChildY );
    }
}

function enableGrafterPostLabelOptions() {

    $('#labelProgressBar').empty().remove();
    $('#labelProgressBarHeader').remove();

}


function grafterAsyncAddLabels( p ) {

    $('#labelProgressBar').progressbar( 'option', 'value', ( p.index / p.labels.length ) * 100 );

    p.labelHolder[ p.labels[p.index].id ] =
        drawCladeLabel( { canvas: p.canvas, localConfig: p.localConfig, label: p.labels[p.index++] } );

    if( p.index < p.labels.length ) {

        setTimeout( grafterAsyncAddLabels, 0, { labels: p.labels, index: p.index, labelHolder: p.labelHolder, scaling: p.scaling,
                                         canvas: p.canvas, localConfig: p.localConfig } );

    } else {

        enableGrafterPostLabelOptions();
    }
}

function grafterAddLabels( p ) {

    createProgressBar( { container: $('#mainPanel') } );

    grafterAsyncAddLabels( p );
}
